package xyz.xtt.equity.service.impl;

import static xyz.xtt.common.asset.AssertUtils.isFalse;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import cn.hutool.core.util.IdUtil;
import xyz.xtt.common.starter.cache.CacheManager;
import xyz.xtt.equity.api.enums.BenefitEnum;
import xyz.xtt.equity.api.enums.UserBenefitSourceTypeEnum;
import xyz.xtt.equity.client.request.TblUserBenefitDto;
import xyz.xtt.equity.constants.AssetCache;
import xyz.xtt.equity.constants.ErrorMessage;
import xyz.xtt.equity.entity.TblUserBenefit;
import xyz.xtt.equity.mapper.TblUserBenefitMapper;
import xyz.xtt.equity.service.ITblUserBenefitService;
import xyz.xtt.equity.vo.UserVipVO;

/**
 * <p>
 * 用户权益表 服务实现类
 * </p>
 *
 * @author dale
 * @since 2025-02-10
 */
@Service
public class TblUserBenefitServiceImpl extends ServiceImpl<TblUserBenefitMapper, TblUserBenefit> implements ITblUserBenefitService {
	private static final Integer USER_BENEFIT_NORMAL = 0;
	private static final Integer USER_BENEFIT_BLOCKED = 1;

	@Autowired
	private CacheManager cacheManager;

	@Override
	public TblUserBenefit queryLatestUserBenefit(Long appUserId, BenefitEnum tblBenefitEnum) {
        LambdaQueryChainWrapper<TblUserBenefit> queryChainWrapper = new LambdaQueryChainWrapper<>(baseMapper);
        return queryChainWrapper.eq(TblUserBenefit::getAppUserId, appUserId)
                .eq(TblUserBenefit::getBenefitCode, tblBenefitEnum.getCode())
                .eq(TblUserBenefit::getBenefitStatus,0)
                .eq(TblUserBenefit::getIsDel, 0)
                .orderByDesc(TblUserBenefit::getCreateTime)
                .last("limit 1")
                .one();
    }

	@Override
	public UserVipVO isVip(Long appUserId) {
		UserVipVO result = new UserVipVO();
		TblUserBenefit latestUserBenefit = queryLatestUserBenefit(appUserId, BenefitEnum.NFT_VIP_TAG);
		result.setTblUserBenefit(latestUserBenefit);
		result.setHasVip(latestUserBenefit != null && latestUserBenefit.getExpireAt().isAfter(LocalDateTime.now()));
		return result;
	}

	@Override
	public TblUserBenefitDto queryLatestUserBenefitDto(Long appUserId, BenefitEnum tblBenefitEnum) {
		return convert2UserBenefitDto(queryLatestUserBenefit(appUserId, tblBenefitEnum));
	}

	private TblUserBenefitDto convert2UserBenefitDto(TblUserBenefit userBenefit) {
		if (userBenefit != null) {
			TblUserBenefitDto dto = new TblUserBenefitDto();
			BeanUtils.copyProperties(userBenefit, dto);
			return dto;
		}
		return null;
	}

	@Override
	public List<TblUserBenefit> queryUserBenefits(Long uid, List<Long> benefitIds) {
		LambdaQueryChainWrapper<TblUserBenefit> queryChainWrapper = new LambdaQueryChainWrapper<>(baseMapper);
		return queryChainWrapper.eq(TblUserBenefit::getAppUserId, uid).in(TblUserBenefit::getBenefitId, benefitIds).eq(TblUserBenefit::getIsDel, 0).list();
	}

	@Override
	public List<TblUserBenefit> queryUserValidBenefitsByCode(Long uid, List<String> benefitCodes) {
        LambdaQueryChainWrapper<TblUserBenefit> queryChainWrapper = new LambdaQueryChainWrapper<>(baseMapper);
        return queryChainWrapper.eq(TblUserBenefit::getAppUserId, uid)
                .in(TblUserBenefit::getBenefitCode, benefitCodes)
                .eq(TblUserBenefit::getIsDel, 0)
                .gt(TblUserBenefit::getExpireAt, LocalDateTime.now())
                .gt(TblUserBenefit::getBenefitRemain, 0)
                .eq(TblUserBenefit::getBenefitStatus,USER_BENEFIT_NORMAL)
                .list();
    }

	@Override
	public List<TblUserBenefit> queryUserValidBenefitsByCodeForUpdate(Long uid, List<String> benefitCodes) {
		LambdaQueryChainWrapper<TblUserBenefit> queryChainWrapper = new LambdaQueryChainWrapper<>(baseMapper);
		return queryChainWrapper
				.eq(TblUserBenefit::getAppUserId, uid)
				.in(TblUserBenefit::getBenefitCode, benefitCodes)
				.eq(TblUserBenefit::getIsDel, 0)
				.gt(TblUserBenefit::getExpireAt, System.currentTimeMillis())
				.gt(TblUserBenefit::getBenefitRemain, 0)
				.last("for update")
				.list();
	}

	@Override
	public boolean updateBenefitRemain(Long userBenefitId, int newRemain, int oldRemain) {
		UpdateWrapper<TblUserBenefit> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("user_benefit_id", userBenefitId).eq("benefit_remain", oldRemain).set("benefit_remain", newRemain);
		return baseMapper.update(updateWrapper) > 0;
	}

	@Override
	public TblUserBenefit queryUserInitMintBenefit(long appUserId) {
		return cacheManager.cacheObj(() -> {
            LambdaQueryChainWrapper<TblUserBenefit> queryChainWrapper = new LambdaQueryChainWrapper<>(baseMapper);
            return queryChainWrapper.eq(TblUserBenefit::getAppUserId, appUserId)
                    .eq(TblUserBenefit::getBenefitCode, BenefitEnum.FREE_MINT.getCode())
                    .eq(TblUserBenefit::getSourceType, UserBenefitSourceTypeEnum.USER_INIT_MINT_BENEFIT.name())
                    .eq(TblUserBenefit::getIsDel, 0)
                    .last("limit 1")
                    .one();
        }, AssetCache.USER_INIT_MINT_BENEFIT, String.valueOf(appUserId));
	}

	@Override
	public Boolean hasVip(Long appUid) {
		UserVipVO vip = isVip(appUid);
		return vip.isHasVip() ? Boolean.TRUE : Boolean.FALSE;
	}

	@Override
	public List<TblUserBenefit> queryNotExpiredUserBenefitsByPurchaseRecord(Long recordId) {
		return this
				.lambdaQuery()
				.eq(TblUserBenefit::getBenefitPurchaseRecordId, recordId)
				.gt(TblUserBenefit::getExpireAt, LocalDateTime.now())
				.eq(TblUserBenefit::getIsDel, 0)
				.list();
	}

	private void updateNextUserBenefitValidTime(Long appUserId, String benefitCode, LocalDateTime lastExpireAt, LocalDateTime nextStartTime) {
		LambdaQueryChainWrapper<TblUserBenefit> queryChainWrapper = new LambdaQueryChainWrapper<>(baseMapper);
		List<TblUserBenefit> userBenefits = queryChainWrapper
				.eq(TblUserBenefit::getAppUserId, appUserId)
				.eq(TblUserBenefit::getBenefitCode, benefitCode)
				.eq(TblUserBenefit::getIsDel, 0)
				.ge(TblUserBenefit::getStartTime, lastExpireAt)
				.orderByAsc(TblUserBenefit::getCreateTime)
				.last("for update")
				.list();
		Duration duration = Duration.between(nextStartTime, lastExpireAt);
		LocalDateTime startTime = nextStartTime;
		LocalDateTime now = LocalDateTime.now();
		for (TblUserBenefit userBenefit : userBenefits) {
			userBenefit.setStartTime(startTime);
			userBenefit.setExpireAt(userBenefit.getExpireAt().minus(duration));
			userBenefit.setUpdateTime(now);
			startTime = userBenefit.getExpireAt();
		}
		this.updateBatchById(userBenefits);
	}

	@Override
	public Boolean blockUserBenefitByPurchaseRecord(List<Long> list) {
		LambdaQueryChainWrapper<TblUserBenefit> query = new LambdaQueryChainWrapper<>(baseMapper);
		List<TblUserBenefit> userBenefits = query
				.in(TblUserBenefit::getBenefitPurchaseRecordId, list)
				.eq(TblUserBenefit::getBenefitStatus, USER_BENEFIT_NORMAL)
				.gt(TblUserBenefit::getExpireAt, LocalDateTime.now())
				.eq(TblUserBenefit::getIsDel, 0)
				.list();
		if (CollectionUtils.isEmpty(userBenefits)) {
			return Boolean.FALSE;
		}

		// 修改用户权益状态：已冻结
		LambdaUpdateChainWrapper<TblUserBenefit> update = new LambdaUpdateChainWrapper<>(baseMapper);
		LocalDateTime now = LocalDateTime.now();
		isFalse(update
				.in(TblUserBenefit::getBenefitPurchaseRecordId, list)
				.eq(TblUserBenefit::getIsDel, 0)
				.set(TblUserBenefit::getBenefitStatus, USER_BENEFIT_BLOCKED)
				.set(TblUserBenefit::getBlockTime, LocalDateTime.now())
				.update(), ErrorMessage.USER_BENEFIT_UPDATE_FAILED);

		LocalDateTime maxDate = LocalDateTime.of(9999, 12, 31, 23, 59, 59);
		for (TblUserBenefit userBenefit : userBenefits) {
			if (userBenefit.getExpireAt().truncatedTo(ChronoUnit.SECONDS).isEqual(maxDate) || !userBenefit.getExpireAt().isAfter(LocalDateTime.now())) {
				// 非时效性权益或已经失效权益仅修改冻结状态接口
				continue;
			}
			// 时效性权益需要将后续权益生效时间提前
			updateNextUserBenefitValidTime(userBenefit.getAppUserId(), userBenefit.getBenefitCode(), userBenefit.getExpireAt(), now);
		}
		return Boolean.TRUE;
	}

	@Override
	public Boolean unBlockUserBenefitByPurchaseRecord(List<Long> list) {
		LambdaQueryChainWrapper<TblUserBenefit> query = new LambdaQueryChainWrapper<>(baseMapper);
		List<TblUserBenefit> userBenefits = query
				.in(TblUserBenefit::getBenefitPurchaseRecordId, list)
				.eq(TblUserBenefit::getBenefitStatus, USER_BENEFIT_BLOCKED)
				.eq(TblUserBenefit::getIsDel, 0)
				.list();
		if (CollectionUtils.isEmpty(userBenefits)) {
			return Boolean.FALSE;
		}

		LambdaUpdateChainWrapper<TblUserBenefit> update = new LambdaUpdateChainWrapper<>(baseMapper);
		LocalDateTime now = LocalDateTime.now();
		isFalse(update
				.in(TblUserBenefit::getBenefitPurchaseRecordId, list)
				.eq(TblUserBenefit::getIsDel, 0)
				.set(TblUserBenefit::getBenefitStatus, USER_BENEFIT_NORMAL)
				.set(TblUserBenefit::getBlockTime, now)
				.update(), ErrorMessage.USER_BENEFIT_UPDATE_FAILED);

		LocalDateTime maxDate = LocalDateTime.of(9999, 12, 31, 23, 59, 59);
		List<TblUserBenefit> needInserts = new ArrayList<>();
		for (TblUserBenefit userBenefit : userBenefits) {
			if (userBenefit.getExpireAt().truncatedTo(ChronoUnit.SECONDS).isEqual(maxDate)) {
				// 非时效性权益直接解冻
				continue;
			}
			// 时效性权益解冻时在尾部增加
			TblUserBenefit latestUserBenefit = queryLatestUserBenefit(userBenefit.getAppUserId(), BenefitEnum.getByCode(userBenefit.getBenefitCode()));
			LocalDateTime startTime = LocalDateTime.now();
			if (!Objects.equals(latestUserBenefit.getUserBenefitId(), userBenefit.getUserBenefitId())) {
				startTime = latestUserBenefit.getExpireAt();
			}

			LocalDateTime expireAt;
			if (userBenefit.getBlockTime().isAfter(userBenefit.getExpireAt())) {
				expireAt = startTime;
			} else {
				Duration duration;
				if (userBenefit.getBlockTime().isBefore(userBenefit.getStartTime())) {
					duration = Duration.between(userBenefit.getStartTime(), userBenefit.getExpireAt());
				} else {
					duration = Duration.between(userBenefit.getBlockTime(), userBenefit.getExpireAt());
				}
				expireAt = startTime.plus(duration);
			}

			Duration duration = Duration.between(userBenefit.getBlockTime(), userBenefit.getExpireAt());
			userBenefit.setUserBenefitId(IdUtil.getSnowflakeNextId());
			userBenefit.setBenefitId(IdUtil.getSnowflakeNextId());
			userBenefit.setStartTime(startTime);
			userBenefit.setBenefitNum(userBenefit.getBenefitRemain());
			userBenefit.setBenefitRemain(userBenefit.getBenefitRemain());
			userBenefit.setExpireAt(expireAt);
			userBenefit.setSourceType("unblock from:" + userBenefit.getUserBenefitId());
			userBenefit.setBlockTime(null);
			userBenefit.setBenefitStatus(USER_BENEFIT_NORMAL);
			userBenefit.setCreateTime(LocalDateTime.now());
			needInserts.add(userBenefit);
		}
		if (!CollectionUtils.isEmpty(needInserts)) {
			this.saveBatch(needInserts);
		}
		return Boolean.TRUE;
	}

}
